package com.op.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.op.constants.MenuConstant;
import com.op.constants.MenuTreeBuildStrategy;
import com.op.entity.Menu;
import com.op.entity.MenuJoinRoleMenu;
import com.op.entity.RoleMenu;
import com.op.mapper.MenuMapper;
import com.op.model.vo.ComponentRouterVo;
import com.op.model.vo.NavigationMenuTreeVo;
import com.op.model.vo.SelectMenuVo;
import com.op.service.MenuService;
import com.op.service.RoleMenuService;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * (Menu)表服务实现类
 *
 * @author 孤焰
 * @since 2021-12-25 15:27:28
 */
@Service("menuService")
public class MenuServiceImpl extends MPJBaseServiceImpl<MenuMapper, Menu> implements MenuService {

    @Resource
    MenuMapper menuMapper;
    @Resource
    RoleMenuService roleMenuService;

    @Override
    public List<Menu> selectMenuList(Menu menu) {
        // 为了将name设置为模糊匹配
        String name = menu.getName();
        menu.setName(null);
        // 临时记录菜单类型
        String type = menu.getType();
        menu.setType(null);
        LambdaQueryWrapper<Menu> lambdaQueryWrapper = Wrappers.lambdaQuery(menu);
        lambdaQueryWrapper.like(name != null, Menu::getName, name);
        // 匹配菜单类型
        joinMenuTypeToLambdaQuery(lambdaQueryWrapper, type);

        return menuMapper.selectList(lambdaQueryWrapper);
    }

    @Override
    public List<Menu> selectMenuListJoinRole(Menu menu, Long roleId) {
        MPJLambdaWrapper<Menu> lambdaWrapper = new MPJLambdaWrapper<>();
        lambdaWrapper.selectAll(Menu.class)
                .innerJoin(RoleMenu.class, RoleMenu::getMenuId, Menu::getId)
                .eq(RoleMenu::getRoleId, roleId);
        return menuMapper.selectJoinList(Menu.class, lambdaWrapper);
    }

    @Override
    public List<Menu> selectMenuTree(Menu menu) {
        return buildMenuTree(selectMenuList(menu));
    }

    @Override
    public List<Menu> selectMenuTreeAbsolute(Menu menu) {
        return buildMenuTree(selectMenuList(menu), MenuTreeBuildStrategy.ABSOLUTE_TOP_MENU);
    }

    @Override
    public List<Menu> selectMenuTreeJoinRoleAbsolute(Menu menu, Long roleId) {
        return buildMenuTree(selectMenuListJoinRole(menu, roleId), MenuTreeBuildStrategy.ABSOLUTE_TOP_MENU);
    }

    @Override
    public List<SelectMenuVo> menuTreeLabel(Menu menu) {
        // 组件菜单树
        List<Menu> menuList = selectMenuTree(menu);
        return convertMenuToSelectMenu(menuList);
    }

    @Override
    public boolean hasUniqueByMenuName(Menu menu) {
        LambdaQueryWrapper<Menu> lambdaQueryWrapper = Wrappers.lambdaQuery();
        lambdaQueryWrapper.eq(Menu::getName, menu.getName())
                .eq(Menu::getParentId, menu.getParentId())
                .ne(menu.getId() != null, Menu::getId, menu.getId());
        return menuMapper.selectCount(lambdaQueryWrapper) > 0;
    }

    @Override
    public boolean hasChildMenu(Long id) {
        LambdaQueryWrapper<Menu> lambdaQueryWrapper = Wrappers.lambdaQuery();
        lambdaQueryWrapper.eq(Menu::getParentId, id);
        return menuMapper.selectCount(lambdaQueryWrapper) > 0;
    }

    @Override
    public boolean hasBindingWithRole(Long id) {
        LambdaQueryWrapper<RoleMenu> lambdaQueryWrapper = Wrappers.lambdaQuery();
        lambdaQueryWrapper.eq(RoleMenu::getMenuId, id);
        return roleMenuService.count(lambdaQueryWrapper) > 0;
    }

    @Override
    public List<NavigationMenuTreeVo> navigationMenuTree(Long roleId) {
        Menu menu = new Menu();
        menu.setType("L:M");
        menu.setVisible(MenuConstant.REVEAL);
        menu.setStatus(MenuConstant.ENABLE);
        // TODO 验证基于角色的权限管理的正确性
        // 查询菜单树并转换为导航菜单树的格式
        return convertMenuToNavigationMenuTree(selectMenuTreeJoinRoleAbsolute(menu, roleId));
    }

    @Override
    public List<ComponentRouterVo> componentRouter() {
        // 查询出path component roleIds
        MPJLambdaWrapper<Menu> lambdaWrapper = new MPJLambdaWrapper<>();
        lambdaWrapper.eq(Menu::getType, MenuConstant.MENU_TYPE_OF_MENU)
                .eq(Menu::getStatus, MenuConstant.ENABLE)
                .innerJoin(RoleMenu.class, RoleMenu::getMenuId, Menu::getId)
                .select(Menu::getPath, Menu::getComponent)
                .select(RoleMenu::getRoleId);
        List<MenuJoinRoleMenu> menuJoinRoleMenus = menuMapper.selectJoinList(MenuJoinRoleMenu.class, lambdaWrapper);
        // 根据path处理得出name
        menuJoinRoleMenus.forEach(item ->
                item.setName(resolveComponentNameByPath(item.getPath())));
        // TODO 转换类型
        List<ComponentRouterVo> componentRouterVos = menuJoinRoleMenus.stream()
                .map(ComponentRouterVo::new)
                .collect(Collectors.toList());
        // TODO 合并角色列表
        return mergeComponentRouter(componentRouterVos);
    }

    /**
     * 将菜单列表构建为树形菜单结构
     * @param menuList 菜单列表
     * @return 菜单树列表
     */
    private List<Menu> buildMenuTree(List<Menu> menuList) {
        return buildMenuTree(menuList, MenuTreeBuildStrategy.RELATIVE_TOP_MENU);
    }

    /**
     * 将菜单列表构建为树形菜单结构
     * @param menuList 菜单列表
     * @param buildStrategy 构建策略
     * @return 菜单树列表
     */
    private List<Menu> buildMenuTree(List<Menu> menuList, MenuTreeBuildStrategy buildStrategy) {
        // 存储每个菜单的Id，用于判断当前菜单项是否为顶层菜单
        Set<Long> temp = null;
        if (MenuTreeBuildStrategy.RELATIVE_TOP_MENU.equals(buildStrategy)) {
            temp = menuList.stream()
                    .map(Menu::getId)
                    .collect(Collectors.toSet());
        }

        List<Menu> topMenu = new ArrayList<>();
        for (Menu menu : menuList) {
            menu.setChildren(getChildren(menuList, menu));
            if (MenuTreeBuildStrategy.RELATIVE_TOP_MENU.equals(buildStrategy)) {
                if (!temp.contains(menu.getParentId())) {
                    topMenu.add(menu);
                }
            } else if (MenuTreeBuildStrategy.ABSOLUTE_TOP_MENU.equals(buildStrategy)) {
                if (MenuConstant.TOP_MENU_PARENT_ID.equals(menu.getParentId())) {
                    topMenu.add(menu);
                }
            }
        }

        return reSort(topMenu);
    }

    /**
     * 获取当前菜单的子菜单
     * @param menuList 全部菜单列表
     * @param menu 当前菜单项
     * @return 当前菜单项的子菜单
     */
    private List<Menu> getChildren(List<Menu> menuList, Menu menu) {
        return reSort(menuList.stream()
                .filter(item -> menu.getId().equals(item.getParentId()))
                .collect(Collectors.toList()));
    }

    /**
     * 是否拥有孩子节点
     * @param menu 当前菜单项
     * @return true - 有子菜单 false - 没有子菜单
     */
    private boolean hasChildren(Menu menu) {
        return !CollectionUtils.isEmpty(menu.getChildren());
    }

    /**
     * 将菜单树列表转换为下拉菜单树列表
     * @param menuList 菜单树列表
     * @return 下拉菜单树列表
     */
    private List<SelectMenuVo> convertMenuToSelectMenu(List<Menu> menuList) {
        return menuList.stream()
                .map(SelectMenuVo::new)
                .collect(Collectors.toList());
    }

    /**
     * 将菜单树列表转换为导航菜单树列表
     * @param menuList 菜单树列表
     * @return 导航菜单树列表
     */
    private List<NavigationMenuTreeVo> convertMenuToNavigationMenuTree(List<Menu> menuList) {
        return menuList.stream()
                .map(NavigationMenuTreeVo::new)
                .collect(Collectors.toList());
    }

    /**
     * 排序菜单列表，并重新设置sort值
     * @param menuList
     * @return
     */
    private List<Menu> reSort(List<Menu> menuList) {
        return menuList.stream()
                .sorted(Comparator.comparingInt(Menu::getSort))
                .collect(Collectors.toList());
    }

    /**
     * 根据路由路径解析出组件名
     * @param path 路由路径
     * @return 组件名
     */
    private String resolveComponentNameByPath(String path) {
        if (!path.contains("/")) {
            throw new RuntimeException("解析路由路径转为组件名错误，路由路径不包含\"/\"字符");
        }
        return path.substring(path.lastIndexOf("/"));
    }

    /**
     * 将单条组件路径的角色合并
     * @param componentRouterVoList 组件路由列表
     * @return 组件路由Vo类
     */
    private List<ComponentRouterVo> mergeComponentRouter(List<ComponentRouterVo> componentRouterVoList) {
        // TODO 测试 将角色合并成列表
        Map<ComponentRouterVo, ComponentRouterVo> map = new HashMap<>();
        componentRouterVoList.forEach(item -> {
            if (map.containsKey(item)) {
                ComponentRouterVo componentRouterVo = map.get(item);
                componentRouterVo.getRoleIds().addAll(item.getRoleIds());
            } else {
                map.put(item, item);
            }
        });

        return new ArrayList<>(map.keySet());
    }

    /**
     * 向lambdaQueryWrapper中拼接查询menuType的条件
     * @param lambdaQueryWrapper lambda查询表达式
     * @param type 菜单类型
     */
    private void joinMenuTypeToLambdaQuery(LambdaQueryWrapper<Menu> lambdaQueryWrapper, String type) {
        if (!StringUtils.isEmpty(type)) {
            String[] menuTypes = type.split(":");
            for (int i = 0; i < menuTypes.length; i++) {
                String menuType = menuTypes[i];
                if (!StringUtils.isEmpty(menuType)) {
                    lambdaQueryWrapper.eq(Menu::getType, menuType);
                }
                if (i != menuTypes.length - 1) {
                    lambdaQueryWrapper.or();
                }
            }
        }
    }
}

